#include "Console.h"

#include <cstdio>

namespace CEngine
{

#ifdef WINDOWS

#include <windows.h>

namespace Console{
I32
print(const char* input, ...) {
    char buffer[1024];
    U32 bufferPosition = 0;
    I32 printedCharacters = 0;

    va_list args;
    va_start(args, input);

    ///copy and process each character into the buffer.
    ///Includes a nasty hack to fix %q/%r causing the next character to not be checked if \0.
    for (U32 i = 0; input[i] != '\0' && (i < 1 || input[i - 1] != '\0'); ++i) {
        if(bufferPosition >= 1023) {
            buffer[bufferPosition] = '\0';

            printedCharacters += vprintf(buffer, args);

            bufferPosition = 0;
            --i;
        }
        else if(input[i] == '%') {
            if(input[i + 1] == 'q' || input[i + 1] == 'Q') {
                buffer[bufferPosition] = '\0';

                printedCharacters += vprintf(buffer, args);

                I32 colour = va_arg(args, I32);
                colour = colour & 255;

                TextColour foreground = (TextColour)(colour & 15);
                TextColour background = (TextColour)((colour & 240) >> 4);
                setTextColour(foreground, background);

                bufferPosition = 0;
                ++ ++i;
            }
            else if(input[i + 1] == 'r' || input[i + 1] == 'R') {
                buffer[bufferPosition] = '\0';
                printedCharacters += vprintf(buffer, args);

                TextColour foreground = RESET;
                TextColour background = RESET;
                setTextColour(foreground, background);

                bufferPosition = 0;
                ++ ++i;
            }

            buffer[bufferPosition] = input[i];
            ++bufferPosition;
        }
        else {
            buffer[bufferPosition] = input[i];
            ++bufferPosition;
        }
    }
    buffer[bufferPosition] = '\0';
    printedCharacters += vprintf(buffer, args);
    va_end(args);

    return printedCharacters;
}

I32
vPrint(const char* input, va_list args) {
    char buffer[1024];
    U32 bufferPosition = 0;
    I32 printedCharacters = 0;

    for (U32 i = 0; input[i] != '\0'; ++i) {
        if(bufferPosition >= 1023) {
            buffer[bufferPosition] = '\0';

            printedCharacters += vprintf(buffer, args);

            bufferPosition = 0;
            --i;
        }
        else if(input[i] == '%') {
            if(input[i + 1] == 'q' || input[i + 1] == 'Q') {
                buffer[bufferPosition] = '\0';

                printedCharacters += vprintf(buffer, args);

                I32 colour = va_arg(args, I32);
                colour = colour & 255;

                TextColour foreground = (TextColour)(colour & 15);
                TextColour background = (TextColour)((colour & 240) >> 4);
                setTextColour(foreground, background);

                bufferPosition = 0;
                ++ ++i;
            }
            else if(input[i + 1] == 'r' || input[i + 1] == 'R') {
                buffer[bufferPosition] = '\0';
                printedCharacters += vprintf(buffer, args);

                TextColour foreground = RESET;
                TextColour background = RESET;
                setTextColour(foreground, background);

                bufferPosition = 0;
                ++ ++i;
            }

            buffer[bufferPosition] = input[i];
            ++bufferPosition;
        }
        else {
            buffer[bufferPosition] = input[i];
            ++bufferPosition;
        }
    }
    buffer[bufferPosition] = '\0';
    printedCharacters += vprintf(buffer, args);

    return printedCharacters;
}

I32
sPrint(char* output, U32 length, const char* input, ...) {
    I32 formattedCharacters = 0;
    va_list args;
    va_start(args, input);

    formattedCharacters = vsnprintf(output, length, input, args);

    va_end(args);
    return formattedCharacters;
}

I32
vsPrint(char* output, U32 length, const char* input, va_list args) {
    I32 formattedCharacters = 0;

    formattedCharacters = vsnprintf(output, length, input, args);

    return formattedCharacters;
}

I32
scan(const char* input, ...) {
    I32 argumentsRead = 0;

    va_list args;
    va_start(args, input);

    argumentsRead = vscanf(input, args);
    va_end(args);

    return argumentsRead;
}

I32
vScan(const char* input, va_list args) {
    I32 argumentsRead = 0;

    argumentsRead = vscanf(input, args);

    return argumentsRead;
}

void
setTextColour(const TextColour f, const TextColour b){
    HANDLE handle;
    handle = GetStdHandle(STD_OUTPUT_HANDLE);

    if (f == RESET || b == RESET) {
        SetConsoleTextAttribute(handle, defaultConsoleTextColour);
        return;
    }

    SetConsoleTextAttribute(handle, f + (b * 16));
}

U8
getTextColour(void) {
    HANDLE handle;
    handle = GetStdHandle(STD_OUTPUT_HANDLE);

    CONSOLE_SCREEN_BUFFER_INFO info;
    GetConsoleScreenBufferInfo(handle, &info);

    return info.wAttributes & 255;
}

}

}
#endif
